﻿using System;
using System.IO;
using System.Text;
using System.IO.Compression;
using System.Globalization;

namespace NazTek.Extension.Clr4
{
    /// <summary>
    /// Extends byte arrays
    /// </summary>
    internal static class ByteArrayExtension
    {
        /// <summary> 
        /// Compresses a byte array 
        /// </summary> 
        /// <param name="uncompressedBytes">The byte array to compress</param> 
        /// <returns>Compressed byte array</returns> 
        /// <remarks>Compression is performed using <see cref="GZipStream"/></remarks> 
        internal static byte[] Compress(this byte[] uncompressedBytes)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                using (Stream gz = new GZipStream(ms, CompressionMode.Compress, true))
                    gz.Write(uncompressedBytes, 0, uncompressedBytes.Length);
                ms.Position = 0;
                byte[] compressedData = new byte[(int)ms.Length - 1 + 1];
                ms.Read(compressedData, 0, (int)ms.Length);
                return compressedData;
            }
        }
        /// <summary> 
        /// Decompresses a byte array 
        /// </summary> 
        /// <param name="compressedBytes">The byte array to decompress</param> 
        /// <returns>Decompressed byte array</returns> 
        /// <remarks></remarks> 
        internal static byte[] Decompress(this byte[] compressedBytes)
        {
            byte[] decompressedData = null;
            int totalBytesRead = 0;
            using (MemoryStream ms = new MemoryStream(compressedBytes))
                using (Stream zs = new GZipStream(ms, CompressionMode.Decompress))
                    while (true)
                    {
                        Array.Resize(ref decompressedData, totalBytesRead + compressedBytes.Length + 1);
                        int bytesRead = zs.Read(decompressedData, totalBytesRead, compressedBytes.Length);
                        if (bytesRead == 0)
                            break;
                        totalBytesRead += bytesRead;
                    }
            Array.Resize(ref decompressedData, totalBytesRead);
            return decompressedData;
        }
    }
    /// <summary>
    /// Extends generic arrays
    /// </summary>
    public static class GenericArrayExtension
    {
        /// <summary>
        /// Compares two arrays of a specified type.  It's case sensitive
        /// </summary>
        /// <typeparam name="T">The array item type, enforces comparision between arrays of same item types</typeparam>
        /// <param name="sourceArray">The array to compare</param>
        /// <param name="destinationArray">The array to compare with</param>
        /// <returns>True when the arrays are equal in size and content, otherwise false</returns>
        /// <example>Sample below shows how to use the method.
        /// <code>
        ///   string[] oArray1 = new string[] { "one", "two" };
        ///   string[] oArray2 = new string[] { "one", "two" };
        ///   string[] oArray3 = new string[] { "one", "TWO" };
        ///   string[] oArray4 = new string[] { "one", "two", "three" };
        ///   string[] oArray5 = null;
        ///   
        ///   bool areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray2)); // true
        ///   bool areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray3)); // false
        ///   areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray4)); // false
        ///   areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray5)); // false
        /// </code>
        /// </example>
        public static bool Equals<T>(this T[] sourceArray, T[] destinationArray)
        {
            Parameter.Validate<Array>("destinationArray", destinationArray);

            string sMessage;
            return sourceArray.Equals<T>(destinationArray, out sMessage);
        }
        /// <summary>
        /// Compares two arrays of a specified type and hydrates a failure message when unequal.  It's case sensitive
        /// </summary>
        /// <typeparam name="T">The array item type, enforces comparision between arrays of same item types</typeparam>
        /// <param name="sourceArray">The array to compare</param>
        /// <param name="destinationArray">The array to compare with</param>
        /// <param name="failureMessage">Hydrates with specific failure message </param>
        /// <returns>True when the arrays are equal in size and content, otherwise false</returns>
        /// <example>Sample below shows how to use the method.
        /// <code>
        ///     string[] oArray1 = new string[] { "one", "two" };
        ///     string[] oArray2 = new string[] { "one", "two" };
        ///     string[] oArray3 = new string[] { "one", "TWO" };
        ///     string[] oArray4 = new string[] { "one", "two", "three" };
        ///     string[] oArray5 = null;
        ///     string failureMessage = null;
        ///   
        ///     // true, failureMessage = null
        ///     bool areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray2, out failureMessage);
        ///     // false, failureMessage = "Unequal value: value of source item indexed 1 is [two] 
        ///     // and the value of target item of the same index is [TWO]"
        ///     areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray3, out failureMessage); 
        ///     // false, failureMessage = "Unequal size: source array has 2 elements and the target array has 3 elements"
        ///     areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray4, out failureMessage); 
        ///     // false, failureMessage = "Null array: target array is null"
        ///     areMyArraysEqual = oArray1.Equals&lt;string&gt;(oArray5, out failureMessage); 
        /// </code>
        /// </example>
        public static bool Equals<T>(this T[] sourceArray, T[] destinationArray, out string failureMessage)
        {
            Parameter.Validate<T[]>("destinationArray", destinationArray);

            if (destinationArray == null)
            {
                failureMessage = StringResource.Default.Msg_NullArrayComparision;
                return false;
            }
            if (sourceArray.Length != destinationArray.Length)
            {
                failureMessage = string.Format(CultureInfo.CurrentCulture, StringResource.Default.Msg_UnequalArrayComparision, sourceArray.Length, destinationArray.Length);
                return false;
            }
            for (int i = 0; i < sourceArray.Length; i++)
                if (!sourceArray[i].Equals(destinationArray[i]))
                {
                    failureMessage = string.Format(CultureInfo.CurrentCulture, StringResource.Default.Msg_UnequalContentArrayComparision, i, sourceArray[i], destinationArray[i]);
                    return false;
                }
            failureMessage = null;
            return true;
        }
        /// <summary>
        /// Concatenates the values of an array into a string 
        /// </summary>
        /// <typeparam name="T">Array item type</typeparam>
        /// <param name="sourceArray">The list to concatenate into string</param>
        /// <param name="itemSeparator">Seperates the items with</param>
        /// <param name="compact">Removes null/empty items</param>
        /// <returns>Concatenated items string</returns>
        /// <remarks>Each item is converted to string via object.ToString(), which must be overridden for reference types for accurate results</remarks> 
        /// <example>Sample below shows how to use the method.
        /// <code>
        ///     // Sample 1
        ///     string[] oStringArray = new string[] { "one", "two", "", null, " ", "three" };
        ///     string serialArray = oStringArray.ToString&lt;string&gt;(",", true)); // "one,two,,,,three"
        ///     
        ///     // Sample2
        ///     string[] oStringArray2 = new string[] { "one", "two", null, " ", "three" };
        ///     serialArray = oStringArray2.ToString&lt;string&gt;("|", true)); // "one|two|||three"
        /// </code>
        /// </example>
        public static string ToString<T>(this T[] sourceArray, string itemSeparator, bool compact)
        {
            StringBuilder oRetValue = new StringBuilder();
            Array.ForEach(sourceArray, (T item) => oRetValue.Append(item == null ? "" : item.ToString().Trim()).Append(itemSeparator));
            if (compact)
            {
                string sConsecutiveSeparators = itemSeparator + itemSeparator;
                while (oRetValue.ToString().Contains(sConsecutiveSeparators))
                {
                    oRetValue.Replace(sConsecutiveSeparators, itemSeparator);
                }
            }
            return oRetValue.ToString().TrimEnd(itemSeparator.ToCharArray());
        }
        /// <summary> 
        /// Concatenates the values of an array into a string 
        /// </summary> 
        /// <typeparam name="T">Array item type</typeparam> 
        /// <param name="sourceArray">The list to concatenate into string</param> 
        /// <param name="itemSeparator">Seperates the items with</param> 
        /// <returns>Concatenated items string</returns> 
        /// <remarks>Each item is converted to string via object.ToString(), which must be overridden for reference types for accurate results</remarks> 
        /// <example>Sample below shows how to use the method.
        /// <code>
        ///     // Sample 1
        ///     string[] oStringArray = new string[] { "one", "two", "three" };
        ///     string serialArray = oStringArray.ToString&lt;string&gt;(",")); // "one,two,three"
        ///     
        ///     // Sample2
        ///     int[] oIntArray = new int[] { 1, 2, 3 };
        ///     serialArray = oIntArray.ToString&lt;int&gt;("|")); // "1|2|3"
        /// </code>
        /// </example>
        public static string ToString<T>(this T[] sourceArray, string itemSeparator)
        {
            return sourceArray.ToString<T>(itemSeparator, false);
        }
    }
}
